import {
	sm2,
	sm3,
	sm4
} from '@/node_modules/sm-crypto';
import * as Base64 from 'js-base64';

const cipherMode = 1; // 1 - C1C3C2，0 - C1C2C3，默认为1

class SMUtil {
	/**
	 * SM2 加密
	 * @param {string} publicKey - 公钥（Base64 编码）
	 * @param {string} value - 需要加密的数据
	 * @returns {string} 加密后的数据（Base64 编码）
	 */
	static sm2Encrypt(publicKey, value) {
		let encrypt = sm2.doEncrypt(value, base64URLSafeToHex(publicKey), cipherMode);
		if (!encrypt.startsWith('04')) {
			encrypt = '04' + encrypt;
		}
		return hexToBase64URLSafe(encrypt); // 使用 C1C3C2 模式
	}

	/**
	 * SM2 解密
	 * @param {string} privateKey - 私钥（Base64 编码）
	 * @param {string} value - 加密后的数据（Base64 编码）
	 * @returns {string} 解密后的数据
	 */
	static sm2Decrypt(privateKey, value) {
		let decrypt = base64URLSafeToHex(value);
		if (decrypt.startsWith('04')) {
			decrypt = decrypt.slice(2);
		}
		return sm2.doDecrypt(decrypt, base64URLSafeToHex(privateKey), cipherMode); // 使用 C1C3C2 模式
	}

	/**
	 * SM3 哈希
	 * @param {string} data - 需要哈希的数据
	 * @returns {string} 哈希值（十六进制字符串）
	 */
	static sm3Hash(data) {
		return sm3(data).toUpperCase();
	}

	/**
	 * SM4 加密
	 * @param {string} key - 密钥（Base64 编码）
	 * @param {string} iv - 初始向量（Base64 编码）
	 * @param {string} data - 需要加密的数据
	 * @returns {string} 加密后的数据（Base64 编码）
	 */
	static sm4Encrypt(key, iv, data) {
		return hexToBase64URLSafe(sm4.encrypt(data, base64URLSafeToHex(key), {
			iv: base64URLSafeToHex(iv),
			mode: 'cbc',
			padding: 'pkcs#7'
		}));
	}

	/**
	 * SM4 解密
	 * @param {string} key - 密钥（Base64 编码）
	 * @param {string} iv - 初始向量（Base64 编码）
	 * @param {string} encryptedData - 加密后的数据（Base64 编码）
	 * @returns {string} 解密后的数据
	 */
	static sm4Decrypt(key, iv, encryptedData) {
		return sm4.decrypt(base64URLSafeToHex(encryptedData), base64URLSafeToHex(key), {
			iv: base64URLSafeToHex(iv),
			mode: 'cbc',
			padding: 'pkcs#7'
		});
	}

	/**
	 * 加密（与后端配套）
	 * @param {string} publicKey - SM2 公钥（Base64 编码）
	 * @param {string} sm4Key - SM4 密钥（Base64 编码）
	 * @param {string} sm4Iv - SM4 初始向量（Base64 编码）
	 * @param {Object} data - 需要加密的数据
	 * @returns {string} 加密后的数据（Base64 编码）
	 */
	static encrypt(publicKey, sm4Key, sm4Iv, data) {
		const dataStr = JSON.stringify(data);
		// SM2 加密
		const encryptedData = this.sm2Encrypt(publicKey, dataStr);
		if (!encryptedData) return null;
		// SM3 加签
		const sign = this.sm3Hash(dataStr);
		if (!sign) return null;
		// SM4 加密
		const jsonStr = JSON.stringify({
			data: encryptedData,
			sign
		});
		return this.sm4Encrypt(sm4Key, sm4Iv, jsonStr);
	}

	/**
	 * 解密（与后端配套）
	 * @param {string} privateKey - SM2 私钥（Base64 编码）
	 * @param {string} sm4Key - SM4 密钥（Base64 编码）
	 * @param {string} sm4Iv - SM4 初始向量（Base64 编码）
	 * @param {string} encryptedData - 加密后的数据（Base64 编码）
	 * @returns {Object} 解密后的数据
	 */
	static decrypt(privateKey, sm4Key, sm4Iv, encryptedData) {
		// SM4 解密
		const jsonStr = this.sm4Decrypt(sm4Key, sm4Iv, encryptedData);
		if (!jsonStr) return null;
		const json = JSON.parse(jsonStr);
		const encrypted = json.data;
		const sign = json.sign;
		if (!encrypted || !sign) return null;
		// SM2 解密
		const decryptedData = this.sm2Decrypt(privateKey, encrypted);
		// SM3 验签
		return this.sm3Hash(decryptedData) === sign ? JSON.parse(decryptedData) : null;
	}
}

// 16 进制字符串转 URL-safe Base64
function hexToBase64URLSafe(hex) {
	// 将十六进制字符串转换为字节数组
	const bytes = new Uint8Array(hex.length / 2);
	for (let i = 0; i < hex.length; i += 2) {
		bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
	}

	const base64 = Base64.fromUint8Array(bytes);

	// 返回 URL 安全的 Base64 编码
	return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}

// URL-safe Base64 转 16 进制字符串
function base64URLSafeToHex(base64) {
	const standardBase64 = base64.replace(/-/g, '+').replace(/_/g, '/');
	const padding = standardBase64.length % 4;
	const paddedBase64 = standardBase64 + '='.repeat((4 - padding) % 4);
	// const byteString = atob(paddedBase64);
	// const bytes = new Uint8Array(byteString.length);
	// for (let i = 0; i < byteString.length; i++) {
	// 	bytes[i] = byteString.charCodeAt(i);
	// }
	// let hex = '';
	// for (let i = 0; i < bytes.length; i++) {
	// 	const hexByte = bytes[i].toString(16).padStart(2, '0');
	// 	hex += hexByte;
	// }

	const byteArray = Base64.toUint8Array(paddedBase64);
	let hex = '';
	for (const byte of byteArray) {
		hex += byte.toString(16).padStart(2, '0'); // 转换为16进制字符串
	}

	return hex;
}

export default SMUtil;